﻿// Admin.NET 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。使用本项目应遵守相关法律法规和许可证的要求。
//
// 本项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。许可证位于源代码树根目录中的 LICENSE-MIT 和 LICENSE-APACHE 文件。
//
// 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动！任何基于本项目二次开发而产生的一切法律纠纷和责任，我们不承担任何责任！

using Admin.NET.Application.Entity;
using Admin.NET.Core.Service;
using DotNetty.Transport.Channels;
using Elastic.Clients.Elasticsearch.Nodes;
using NewLife;
using NewLife.Data;
using NewLife.Net.Modbus1;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Admin.NET.Application.Service.ModbusTcpService.ModbusTcpService;

namespace Admin.NET.Application.Service.ModbusTcp;

public class ModbusConnectInfo
{
    public ModbusConnectStatus ModbusConnectStatus { get; set; }
    public string Ip { get; set; }
    public int Port { get; set; }
    public string Sn { get; set; }
    public string Pwd { get; set; }

    public int UnProcessTime { get; set; } = 0;

    private IChannelHandlerContext channelHandlerContext;

    public IChannelHandlerContext ChannelHandlerContext
    {
        get { return channelHandlerContext; }
        set { channelHandlerContext = value; MyMaster.ChannelHandlerContext = value; }
    }

    /// <summary>
    /// 下发指令的时候使用
    /// </summary>
    public MyMaster MyMaster { get; set; } = new MyMaster();

    public object MyMasterLock = new object();

    #region 下发处理

    #region 点位读写

    public object ReadByPoint(DeviceSlaveTemplatePoints deviceSlaveTemplatePoints, int host)
    {
        lock (this)
        {
            MyMaster.Host = (byte)host;
            var addr = Convert.ToInt32(deviceSlaveTemplatePoints.reg) - 1;
            var length = GetDataLengthByPointType(deviceSlaveTemplatePoints.DataType);
            switch (deviceSlaveTemplatePoints.RegType)
            {
                case RegType.Coil:
                    return ReadCoil(addr);

                case RegType.DiscreteInput:
                    return ReadInputs(addr, 1);

                case RegType.HoldingRegister:
                    return ReadHoldingRegisters(addr, (ushort)length);

                case RegType.InputRegister:
                    return ReadInputRegisters(addr, (ushort)length);

                default:
                    break;
            }

            return null;
        }
    }

    public object WriteByPoint(DeviceSlaveTemplatePoints deviceSlaveTemplatePoints, object value, int host)
    {
        lock (MyMasterLock)
        {
            MyMaster.Host = (byte)host;
            var addr = Convert.ToInt32(deviceSlaveTemplatePoints.reg);
            var length = GetDataLengthByPointType(deviceSlaveTemplatePoints.DataType);
            switch (deviceSlaveTemplatePoints.RegType)
            {
                case RegType.Coil:
                    return WriteSingleCoil(addr, Convert.ToBoolean(value));

                case RegType.HoldingRegister:
                    var longValue = Convert.ToInt64(value);
                    UInt16[] values = new UInt16[length];
                    if (length == 1)
                    {
                        values[0] = (UInt16)(longValue & 0xFFFF);
                        return WriteMultipleRegisters(addr, values);
                    }
                    else if (length == 2)
                    {
                        values[0] = (UInt16)(longValue & 0xFFFF);
                        values[1] = (UInt16)((longValue >> 16) & 0xFFFF);
                        return WriteMultipleRegisters(addr, values);
                    }
                    break;

                default:
                    break;
            }

            return false;
        }
    }

    /// <summary>
    /// 将读取到的值转换为字符串
    /// </summary>
    /// <param name="result"></param>
    /// <returns></returns>
    public string ReadValueToString(object result)
    {
        if (result is bool)
            return result + "";
        if (result is byte[])
            return ((byte[])result).ToHex();
        if (result is ushort[])
        {
            var resultArray = (ushort[])result;
            var byteArray = new byte[resultArray.Length * 2];
            for (global::System.Int32 i = 0; i < resultArray.Length; i++)
            {
                var array = NewLife.Net.Modbus1.IOHelper.Uint16ToByte(resultArray[i]);
                byteArray[0 + i * 2] = array[0];
                byteArray[1 + i * 2] = array[1];
            }

            return ByteArrayToInt(byteArray) + "";
        }
        if (result is bool[] || result is Boolean[])
        {
            var boolArray = result as bool[];
            var message = string.Join(" ", boolArray);
            return message;
        }

        return result + "";
    }

    public int ByteArrayToInt(byte[] byteArray)
    {
        if (byteArray == null || byteArray.Length == 0 || byteArray.Length > 4)
        {
            //todo 这里抛异常后shutdown
            return 0;
        }

        int result = 0;
        for (int i = 0; i < byteArray.Length; i++)
        {
            result |= byteArray[i] << (8 * (byteArray.Length - 1 - i));
        }

        return result;
    }

    #endregion 点位读写

    #region 线圈

    /// <summary>读取线圈状态</summary>
    /// <remarks>
    /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
    /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
    /// </remarks>

    /// <param name="addr"></param>
    /// <returns></returns>
    public Boolean ReadCoil(Int32 addr)
    {
        // 小心内部可能没有返回
        return MyMaster.ReadCoil(addr);
    }

    /// <summary>读取线圈状态</summary>
    /// <remarks>
    /// 请求：0x01|2字节起始地址|2字节线圈数量(1~2000)
    /// 响应：0x01|1字节字节计数|n字节线圈状态（n=输出数量/8，如果余数不为0，n=n+1）
    /// </remarks>

    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public Boolean[] ReadCoils(Int32 addr, UInt16 count)
    {
        return MyMaster.ReadCoils(addr, count);
    }

    /// <summary>读取离散量输入</summary>
    /// <remarks>
    /// 请求：0x02|2字节起始地址|2字节输入数量(1~2000)
    /// 响应：0x02|1字节字节计数|n字节输入状态（n=输入数量/8，如果余数不为0，n=n+1）
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public Boolean[] ReadInputs(Int32 addr, UInt16 count)
    {
        return MyMaster.ReadInputs(addr, count);
    }

    /// <summary>写单个线圈</summary>
    /// <remarks>
    /// 请求：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
    /// 响应：0x05|2字节输出地址|2字节输出值（0x0000/0xFF00）
    /// </remarks>

    /// <param name="addr"></param>
    /// <param name="flag"></param>
    /// <returns></returns>
    public Boolean WriteSingleCoil(Int32 addr, Boolean flag)
    {
        return MyMaster.WriteSingleCoil(addr, flag);
    }

    /// <summary>写多个线圈</summary>
    /// <remarks>
    /// 请求：0x0F|2字节起始地址|2字节输出数量（1~1698）|1字节字节计数|n字节输出值（n=输出数量/8，如果余数不为0，n=n+1）
    /// 响应：0x0F|2字节起始地址|2字节输出数量
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <param name="flags"></param>
    /// <returns></returns>
    public Boolean WriteMultipleCoils(Int32 addr, params Boolean[] flags)
    {
        return MyMaster.WriteMultipleCoils(addr, flags);
    }

    #endregion 线圈

    #region 寄存器

    /// <summary>读取保持寄存器</summary>
    /// <remarks>
    /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
    /// 响应：0x03|1字节字节数|n*2字节寄存器值
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <returns></returns>
    public UInt16 ReadHoldingRegister(Int32 addr)
    {
        return MyMaster.ReadHoldingRegister(addr);
    }

    /// <summary>读取保持寄存器</summary>
    /// <remarks>
    /// 请求：0x03|2字节起始地址|2字节寄存器数量（1~2000）
    /// 响应：0x03|1字节字节数|n*2字节寄存器值
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public UInt16[] ReadHoldingRegisters(Int32 addr, UInt16 count)
    {
        return MyMaster.ReadHoldingRegisters(addr, count);
    }

    /// <summary>读取输入寄存器</summary>
    /// <remarks>
    /// 请求：0x04|2字节起始地址|2字节输入寄存器数量（1~2000）
    /// 响应：0x04|1字节字节数|n*2字节输入寄存器
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <param name="count">数量</param>
    /// <returns></returns>
    public UInt16[] ReadInputRegisters(Int32 addr, UInt16 count)
    {
        return MyMaster.ReadInputRegisters(addr, count);
    }

    /// <summary>写单个寄存器</summary>
    /// <remarks>
    /// 请求：0x06|2字节寄存器地址|2字节寄存器值
    /// 响应：0x06|2字节寄存器地址|2字节寄存器值
    /// </remarks>
    /// <param name="modbusConnectInfo"></param>
    /// <param name="addr"></param>
    /// <param name="val"></param>
    /// <returns></returns>
    public Boolean WriteSingleRegister(Int32 addr, UInt16 val)
    {
        return MyMaster.WriteSingleRegister(addr, val);
    }

    /// <summary>写多个寄存器</summary>
    /// <remarks>
    /// 请求：0x10|2字节起始地址|2字节寄存器数量（1~123）|1字节字节计数|n*2寄存器值
    /// 响应：0x10|2字节起始地址|2字节寄存器数量
    /// </remarks>

    /// <param name="addr"></param>
    /// <param name="vals"></param>
    /// <returns></returns>
    public Boolean WriteMultipleRegisters(Int32 addr, params UInt16[] vals)
    {
        return MyMaster.WriteMultipleRegisters(addr, vals);
    }

    #endregion 寄存器

    #endregion 下发处理

    public int GetDataLengthByPointType(Entity.DataType dataType)
    {
        var dataLength = 0;
        switch (dataType)
        {
            case Entity.DataType.DoubleABCD:
                dataLength = 2; // LongABCD 占用8个字节
                break;

            case Entity.DataType.UShort:
                dataLength = 1; break;
            case Entity.DataType.Bit:
                dataLength = 1; break;
            default:
                // 处理未知数据类型的情况
                dataLength = 2;
                break;
        }

        return dataLength;
    }
}

// ModbusConnectInfo连接状态的枚举
public enum ModbusConnectStatus
{
    /// <summary>
    /// 连接中
    /// </summary>
    Connecting = 0,

    /// <summary>
    /// 连接成功
    /// </summary>
    Connected = 1,

    /// <summary>
    /// 连接失败
    /// </summary>
    ConnectFailed = 2,

    /// <summary>
    /// 断开连接
    /// </summary>
    Disconnected = 3,

    NoConfig = 4
}